//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <string.h>
#include <elapi.h>

#include "marshal.h"
#include "prxstub.h"
#include "rot.h"

#define ROT_HASHMASK_BIT    5

#define ROT_HASHMASK        ((1 << ROT_HASHMASK_BIT) - 1)
#define ROT_HASHTABLE_SIZE  (1 << ROT_HASHMASK_BIT)
#define ROT_HASH_GETOIDINDEX(oid_t)  (oid_t & ROT_HASHMASK)

CriticalSection g_importTableLock;
CriticalSection g_exportTableLock;
CriticalSection g_marshalLock;

// global marshal vtbl
//
UInt32 g_marshalVtbl[MSH_MAX_METHODS];

static ImportObject s_hashImportObjects[ROT_HASHTABLE_SIZE];
static ExportObject s_hashExportObjects[ROT_HASHTABLE_SIZE];

ECode InitializeROT()
{
    for (int n = 0; n < ROT_HASHTABLE_SIZE; n++) {
        s_hashImportObjects[n].Initialize();
        s_hashImportObjects[n].m_oid = 0;
        s_hashImportObjects[n].m_pNext = NULL;
        s_hashExportObjects[n].Initialize();
        s_hashExportObjects[n].m_oid = 0;
        s_hashExportObjects[n].m_pNext = NULL;
    }
    _CCriticalSection_Initialize(&g_importTableLock);
    _CCriticalSection_Initialize(&g_exportTableLock);
    _CCriticalSection_Initialize(&g_marshalLock);

    // Hook all interfaces' IInterface to CObjectProxy except Aggregate and
    // Fill v-table with proxy entry.
    // The proxy entrys's array begin from PROXY_ENTRY_BASE (0x7ffef000).
    //
    g_marshalVtbl[0] = (UInt32)&CInterfaceProxy::S_Probe;
    g_marshalVtbl[1] = (UInt32)&CInterfaceProxy::S_AddRef;
    g_marshalVtbl[2] = (UInt32)&CInterfaceProxy::S_Release;
    for (int m = 3; m < MSH_MAX_METHODS; m++) {
        g_marshalVtbl[m] = PROXY_ENTRY_BASE + ((m - 3) << PROXY_ENTRY_SHIFT);
    }

    return NOERROR;
}

ECode RegisterExportObject(
    /* [in] */ oid_t oid,
    /* [in] */ IInterface *pObject,
    /* [in] */ IStub *pIStub)
{
    ExportObject* pObjNode;

    pObject = pObject->Probe(EIID_IInterface);
    if (!pObject) return E_NO_INTERFACE;

    pObjNode = new ExportObject;
    if (NULL == pObjNode) return E_OUT_OF_MEMORY;
    pObjNode->m_oid = oid;
    pObjNode->m_pObject = pObject;
    pObjNode->m_pIStub = pIStub;

    Void * pContext = _Elastos_CriticalSection_Enter(&g_exportTableLock);
    s_hashExportObjects[ROT_HASH_GETOIDINDEX(oid)].InsertFirst(pObjNode);
    _Elastos_CriticalSection_Leave(&g_exportTableLock, pContext);
    return NOERROR;
}

ECode FindExportObject(
    /* [in] */ IInterface *pObject,
    /* [out] */ ExportObject *pExport)
{
    pObject = pObject->Probe(EIID_IInterface);
    if (!pObject) return E_NO_INTERFACE;

    Void * pContext = _Elastos_CriticalSection_Enter(&g_exportTableLock);
    ExportObject* pIter;
    for (int n = 0; n < ROT_HASHTABLE_SIZE; n++) {
        pIter = s_hashExportObjects + n;
        for (; NULL != pIter; pIter = (ExportObject*)(pIter->Next())) {
            if (pIter->m_oid != 0 && pIter->m_pObject == pObject) {
                memcpy(pExport, pIter, sizeof(ExportObject));
                pExport->m_pIStub->AddRef();
                _Elastos_CriticalSection_Leave(&g_exportTableLock, pContext);
                return NOERROR;
            }
        }
    }

    _Elastos_CriticalSection_Leave(&g_exportTableLock, pContext);
    return E_NO_EXPORT_SERVER;
}

ECode FindExportObject(
    /* [in] */ oid_t oid,
    /* [out] */ ExportObject *pExport)
{
    Void * pContext = _Elastos_CriticalSection_Enter(&g_exportTableLock);
    ExportObject* pHead = s_hashExportObjects + ROT_HASH_GETOIDINDEX(oid);

    for (ExportObject* pIter = pHead; NULL != pIter; \
        pIter = (ExportObject*)(pIter->Next())) {
        if (pIter->m_oid == oid) {
            memcpy(pExport, pIter, sizeof(ExportObject));
            pExport->m_pIStub->AddRef();
            _Elastos_CriticalSection_Leave(&g_exportTableLock, pContext);
            return NOERROR;
        }
    }

    _Elastos_CriticalSection_Leave(&g_exportTableLock, pContext);
    return E_NO_EXPORT_SERVER;
}

ECode UnregisterExportObject(
    /* [in] */ oid_t oid)
{
    Void * pContext = _Elastos_CriticalSection_Enter(&g_exportTableLock);
    ExportObject* pHead = s_hashExportObjects + ROT_HASH_GETOIDINDEX(oid);
    ExportObject* pPrev = NULL;

    for (ExportObject* pIter = pHead; NULL != pIter; \
        pIter = (ExportObject*)(pIter->Next())) {
        if (pIter->m_oid == oid) {
            if ((Int32)(((CObjectStub*)(pIter->m_pIStub))->m_cRefs) != 0) {
                _Elastos_CriticalSection_Leave(&g_exportTableLock, pContext);
                return S_FALSE;
            }

            pIter->m_oid = 0;
            if (pIter != pHead) {
                pIter->Detach(pPrev);
                delete pIter;
            }
            _Elastos_CriticalSection_Leave(&g_exportTableLock, pContext);
            return NOERROR;
        }
        pPrev = pIter;
    }

    _Elastos_CriticalSection_Leave(&g_exportTableLock, pContext);
    return E_NO_EXPORT_SERVER;
}

ECode RegisterImportObject(
    /* [in] */ oid_t oid,
    /* [in] */ IProxy *pIProxy)
{
    ImportObject * pObjNode;

    pObjNode = new ImportObject;
    if (NULL == pObjNode) {
        return E_OUT_OF_MEMORY;
    }
    pObjNode->m_oid = oid;
    pObjNode->m_pIProxy = pIProxy;
    Void * pContext = _Elastos_CriticalSection_Enter(&g_importTableLock);
    s_hashImportObjects[ROT_HASH_GETOIDINDEX(oid)].InsertFirst(pObjNode);
    _Elastos_CriticalSection_Leave(&g_importTableLock, pContext);

    return NOERROR;
}

ECode FindImportObject(
    /* [in] */ oid_t oid,
    /* [out] */ ImportObject *pImport)
{
    Void * pContext = _Elastos_CriticalSection_Enter(&g_importTableLock);
    ImportObject* pHead = s_hashImportObjects + ROT_HASH_GETOIDINDEX(oid);

    for (ImportObject* pIter = pHead; NULL != pIter; \
        pIter = (ImportObject*)(pIter->Next())) {
        if (pIter->m_oid == oid) {
            memcpy(pImport, pIter, sizeof(ImportObject));
            pImport->m_pIProxy->AddRef();
            _Elastos_CriticalSection_Leave(&g_importTableLock, pContext);
            return NOERROR;
        }
    }

    _Elastos_CriticalSection_Leave(&g_importTableLock, pContext);
    return E_NO_IMPORT_SERVER;
}

// BUGBUG: must atomic operation with delete proxy
ECode UnregisterImportObject(
    /* [in] */ oid_t oid)
{
    Void * pContext = _Elastos_CriticalSection_Enter(&g_importTableLock);
    ImportObject* pHead = s_hashImportObjects + ROT_HASH_GETOIDINDEX(oid);
    ImportObject* pPrev = NULL;

    for (ImportObject* pIter = pHead; NULL != pIter; \
        pIter = (ImportObject*)(pIter->Next())) {
        if (pIter->m_oid == oid) {
            if ((Int32)(((CObjectProxy *)(pIter->m_pIProxy))->m_cRefs) != 0) {
                _Elastos_CriticalSection_Leave(&g_importTableLock, pContext);
                return S_FALSE;
            }

            pIter->m_oid = 0;
            if (pIter != pHead) {
                pIter->Detach(pPrev);
                delete pIter;
            }

            _Elastos_CriticalSection_Leave(&g_importTableLock, pContext);
            return NOERROR;
        }
        pPrev = pIter;
    }

    _Elastos_CriticalSection_Leave(&g_importTableLock, pContext);
    return E_NO_IMPORT_SERVER;
}
